package kz.pompei.ifacer.visio.skin.paint.check;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JOptionPane;

import kz.pompei.ifacer.grot.acts.Act;
import kz.pompei.ifacer.grot.acts.ChangeFieldsAct;
import kz.pompei.ifacer.model.CursorInfo;
import kz.pompei.ifacer.model.CursorKind;
import kz.pompei.ifacer.model.PaintKind;
import kz.pompei.ifacer.model.widgets.Align;
import kz.pompei.ifacer.model.widgets.CheckState;
import kz.pompei.ifacer.model.widgets.WidgetCheck;
import kz.pompei.ifacer.visio.PaintUtil;
import kz.pompei.ifacer.visio.WidgetProp;
import kz.pompei.ifacer.visio.skin.SkinGetter;

public class PaintWidgetCheck {
  
  private static final int DIST = 4;
  private static final int TOFS = 3;
  
  private final Map<String, Image> imageCache = new HashMap<>();
  @SuppressWarnings("unused")
  private final SkinGetter skin;
  
  public PaintWidgetCheck(SkinGetter skin) {
    this.skin = skin;
  }
  
  private Image getImage(CheckState state) throws Exception {
    {
      Image ret = imageCache.get(state.name());
      if (ret != null) return ret;
    }
    {
      Image ret = ImageIO.read(getClass().getResourceAsStream(state.name() + ".png"));
      imageCache.put(state.name(), ret);
      return ret;
    }
  }
  
  public void paint(Graphics2D g, WidgetCheck w, PaintKind paintKind, Point mouse,
      Rectangle allSpace) throws Exception {
    int height = getHeight(g, w);
    
    Rectangle ir = getIndicatorRect(g, w);
    {
      if (paintKind == PaintKind.MOVING) {
        g.setColor(Color.GRAY);
        g.drawRect(ir.x, ir.y, ir.width, ir.height);
      } else {
        g.drawImage(getImage(w.state), ir.x, ir.y, ir.width, ir.height, null);
        if (paintKind.isOver()) {
          Rectangle mr = getMarkerRect(g, w);
          g.setColor(Color.WHITE);
          g.fillRect(mr.x, mr.y, mr.width, mr.height);
          g.setColor(Color.BLACK);
          g.drawRect(mr.x, mr.y, mr.width, mr.height);
        }
      }
    }
    
    Rectangle tr = getTextRect(g, w);
    {
      if (w.label == null || w.label.trim().length() == 0) {
        if (paintKind != PaintKind.MOVING) {
          g.setColor(Color.YELLOW);
          g.fillRect(tr.x, tr.y, tr.width, tr.height);
        }
      } else {
        g.setColor(paintKind == PaintKind.MOVING ? Color.GRAY :Color.BLACK);
        g.drawString(w.label, tr.x, w.y + height - TOFS);
      }
    }
    
    if (paintKind.isSel()) {
      PaintUtil.drawSelectionRect(g, ir.union(tr));
    }
  }
  
  private Rectangle getTextRect(Graphics2D g, WidgetCheck w) {
    Rectangle ir = getIndicatorRect(g, w);
    
    int width = getTextWidth(g, w.label);
    int height = getHeight(g, w);
    
    if (w.side == Align.LEFT) {
      return new Rectangle(ir.x + ir.width + DIST, ir.y, width, height);
    }
    if (w.side == Align.RIGHT) {
      return new Rectangle(ir.x - DIST - width, ir.y, width, height);
    }
    
    throw new IllegalArgumentException("Unknown value: w.side = " + w.side);
  }
  
  private Rectangle getIndicatorRect(Graphics2D g, WidgetCheck w) {
    int height = getHeight(g, w);
    if (w.side == Align.LEFT) {
      return new Rectangle(w.x, w.y, height + 1, height + 1);
    }
    if (w.side == Align.RIGHT) {
      return new Rectangle(w.x - height, w.y, height + 1, height + 1);
    }
    
    throw new IllegalArgumentException("Unknown value: w.side = " + w.side);
  }
  
  private Rectangle getMarkerRect(Graphics2D g, WidgetCheck w) {
    Rectangle ir = getIndicatorRect(g, w);
    
    int width = ir.width / 3;
    if (w.side == Align.LEFT) {
      return new Rectangle(ir.x, ir.y, width, ir.height);
    }
    if (w.side == Align.RIGHT) {
      return new Rectangle(ir.x + ir.width - width, ir.y, width, ir.height);
    }
    
    throw new IllegalArgumentException("Unknown value: w.side = " + w.side);
  }
  
  private int getTextWidth(Graphics2D g, String text) {
    if (text == null || text.trim().length() == 0) return 5;
    return g.getFontMetrics().stringWidth(text);
  }
  
  private int getHeight(Graphics2D g, WidgetCheck w) {
    return g.getFontMetrics().getHeight();
  }
  
  public Rectangle getWidgetBound(Graphics2D g, WidgetCheck w) {
    return getIndicatorRect(g, w).union(getTextRect(g, w));
  }
  
  public CursorInfo getCursorInfo(Graphics2D g, WidgetCheck w, Point mouse) {
    
    if (getMarkerRect(g, w).contains(mouse)) {
      return new CursorInfo(CursorKind.MOVE, "swit");
    }
    
    Rectangle tr = getTextRect(g, w);
    if (tr.contains(mouse)) {
      return new CursorInfo(CursorKind.MOVE, "edit");
    }
    
    if (tr.union(getIndicatorRect(g, w)).contains(mouse)) {
      return new CursorInfo(CursorKind.MOVE, "chan");
    }
    
    return null;
  }
  
  public Act move(Graphics2D g, WidgetCheck original, WidgetCheck copy, Point fromPoint,
      Point toPoint) throws Exception {
    copy.takePosition(original);
    copy.move(toPoint.x - fromPoint.x, toPoint.y - fromPoint.y);
    return new ChangeFieldsAct(original.uuid, "x", copy.x, "y", copy.y);
  }
  
  public Act getDoubleClickAct(Graphics2D g, WidgetCheck w, Point mouse, JComponent owner)
      throws Exception {
    
    if (getMarkerRect(g, w).contains(mouse)) return changeSide(w, getWidgetBound(g, w).width);
    
    Rectangle tr = getTextRect(g, w);
    if (tr.contains(mouse)) return changeLabel(w, owner);
    
    if (tr.union(getIndicatorRect(g, w)).contains(mouse)) return changeState(w);
    
    return null;
  }
  
  private Act changeState(WidgetCheck w) {
    return new ChangeFieldsAct(w.uuid, "state", w.state.next());
  }
  
  private Act changeLabel(WidgetCheck w, JComponent owner) {
    String newLabel = JOptionPane.showInputDialog(owner, "Укажите новое название", w.label);
    if (newLabel == null) return null;
    return new ChangeFieldsAct(w.uuid, "label", newLabel);
  }
  
  private Act changeSide(WidgetCheck w, int width) {
    if (w.side == Align.LEFT) {
      return new ChangeFieldsAct(w.uuid, "side", Align.RIGHT, "x", w.x + width);
    }
    if (w.side == Align.RIGHT) {
      return new ChangeFieldsAct(w.uuid, "side", Align.LEFT, "x", w.x - width);
    }
    throw new IllegalArgumentException("Unknown value: w.side = " + w.side);
  }
  
  public Act setProperty(Graphics2D g, WidgetCheck w, WidgetProp prop, int value) throws Exception {
    ///////////////////////////////
    if (
    
    (prop == WidgetProp.LEFT && w.side == Align.LEFT)
    
    ||
    
    (prop == WidgetProp.RIGHT && w.side == Align.RIGHT)
    
    ) {
      return new ChangeFieldsAct(w.uuid, "x", value);
    }
    ///////////////////////////////
    if (prop == WidgetProp.LEFT && w.side == Align.RIGHT) {
      Rectangle b = getWidgetBound(g, w);
      return new ChangeFieldsAct(w.uuid, "x", value + b.width);
    }
    ///////////////////////////////
    if (prop == WidgetProp.RIGHT && w.side == Align.LEFT) {
      Rectangle b = getWidgetBound(g, w);
      return new ChangeFieldsAct(w.uuid, "x", value - b.width);
    }
    ///////////////////////////////
    
    if (prop == WidgetProp.TOP) {
      return new ChangeFieldsAct(w.uuid, "y", value);
    }
    if (prop == WidgetProp.BOTTOM) {
      Rectangle b = getWidgetBound(g, w);
      return new ChangeFieldsAct(w.uuid, "y", value - b.height);
    }
    
    return null;
  }
}
